/*
 * @Author: 0x9DEFA478
 * @Date: 2022-01-23 15:21:43
 * @LastEditTime: 2022-03-18 23:50:22
 * @LastEditors: 0x9DEFA478
 * @Description: 
 * QQ:2652450237
 * ============================================================================================================================================
 * 
 * 
 * 
 *                                                                                               ************      ****************************
 *                                                                                             ************      ****************************  
 *                                                                                           ************      ****************************    
 *                                                                                         ************      ****************************      
 *                                                                                       ************      ************                        
 *                                                                                     ************      ************                          
 *                                                                                   ************      ************                            
 *                                                                                 ************      ************                              
 *                                                                               ************      ************                                
 *                                                                             ************      ************                                  
 *                                                                           ************      ************                                    
 *                                                                         ************      ************                                      
 *                                                                       ************      ************                                        
 *                                                                     ************      ************                                          
 *                                                                   ************      ************                                            
 *                                                                 ************      ************                                              
 *                                                               ************      ************                                                
 *                                                             ************      ************                                                  
 *                                                           ************      ************                                                    
 *                                                         ************      ************                                                      
 *                                                       ************      ************                                                        
 *                                                     ************      ************                                                          
 *                                                   ************      ************                                                            
 *                                                 ************      ************                                                              
 *                                               ************      ************                                                                
 *                                             ************      ************                                                                  
 *                                           ************      ************                                                                    
 *                                         ************      ************                                                                      
 *                                       ************      ************                                                                        
 *                                     ************      ************                                                                          
 *                                   ************      ************                                                                            
 *                                 ************      ************                                                                              
 *                               ************      ************                                                                                
 *                             ************      ************                                                                                  
 *                           ************      ************                                                                                    
 *                         ************      ************                                                                                      
 *       ****************************      ************                                                                                        
 *     ****************************      ************                                                                                          
 *   ****************************      ************                                                                                            
 * ****************************      ************                                                                                              
 * 
 * 
 * 
 * ============================================================================================================================================
 * 
 */
#include "../H_NES.h"
#include "../Console/H_NES_Console.h"




static int Command(H_NES_Console* Console,H_NES_Message* Message){
  int FunctionIndex;

  if(Console->IsRun==0){
    return vH_NES_Result_Stopped;
  }

  FunctionIndex=Message->FunctionIndex;
  

  if(0!=Console->LL_Function.Thread.MessageSned(Console->MessageQueue.Command,Message)){
    for(;;){
      //不可能出现的情况
    }
  }

  Console->LL_Function.Thread.MessageReceive(Console->MessageQueue.Ack,Message);
  if(Message->FunctionIndex==FunctionIndex){
    return vH_NES_Result_OK;
  }else if(Message->FunctionIndex==vH_NES_Message_FunctionIndex_Stop){
    Console->IsRun=0;
    return vH_NES_Result_Stopped;
  }else{
    for(;;){
      //不可能出现的情况
    }
  }
}




static int CPU_Init(H_NES_Console* Console){

  Console->CPU.Bus.RAM=Console->Memory.Malloc(2048);
  if(Console->CPU.Bus.RAM==NULL){
    goto Error0;
  }

  return 0;

Error0:
  return -1;
}

static void CPU_DeInit(H_NES_Console* Console){
  Console->Memory.Free(Console->CPU.Bus.RAM);
}

static int PPU_Init(H_NES_Console* Console){

  Console->PPU.VRAM=Console->Memory.Malloc(4096);
  if(Console->PPU.VRAM==NULL){
    goto Error0;
  }

  return 0;

Error0:
  return -1;
}

static void PPU_DeInit(H_NES_Console* Console){
  Console->Memory.Free(Console->PPU.VRAM);
}

static int APU_Init(H_NES_Console* Console){

#if vH_NES_APU_Enable != 0
#if vH_NES_APU_OutputChannelSrc == 0
  Console->CPU.APU.Output.Buffer=Console->Memory.Malloc(vH_NES_APU_NumOfOutputSample*sizeof(Hint16));
#else
  Console->CPU.APU.Output.Buffer=Console->Memory.Malloc(vH_NES_APU_NumOfOutputSample*sizeof(Huint32));
#endif
  if(Console->CPU.APU.Output.Buffer==NULL){
    goto Error0;
  }
#endif

  return 0;

#if vH_NES_APU_Enable != 0
Error0:
  return -1;
#endif
}

static void APU_DeInit(H_NES_Console* Console){
#if vH_NES_APU_Enable != 0
  Console->Memory.Free(Console->CPU.APU.Output.Buffer);
#endif
}




/**
 * @brief 新建一个主机
 * @param LL_Function 底层方法
 * @param Malloc 分配内存方法
 * @param Free 释放内存方法
 * @return 新建的主机句柄
 */
void* new_H_NES_Console(H_NES_LL_Function* LL_Function,void* (*Malloc)(Husize),void (*Free)(void*)){

  H_NES_Console* r;

  r=Malloc(sizeof(H_NES_Console));
  if(r==NULL){
    goto Error0;
  }

  r->TelevisionStandards=vH_NES_TelevisionStandards_NTSC;

  r->LL_Function=LL_Function[0];

  r->Memory.Malloc=Malloc;
  r->Memory.Free=Free;

  r->MessageQueue.Command=LL_Function->Thread.new_MessageQueue(1,sizeof(H_NES_Message));
  if(r->MessageQueue.Command==NULL){
    goto Error1;
  }
  r->MessageQueue.Ack=LL_Function->Thread.new_MessageQueue(2,sizeof(H_NES_Message));
  if(r->MessageQueue.Ack==NULL){
    goto Error2;
  }

  if(0!=CPU_Init(r)){
    goto Error3;
  }
  if(0!=PPU_Init(r)){
    goto Error3;
  }
  if(0!=APU_Init(r)){
    goto Error3;
  }

  r->IsRun=0;
  r->CompatibilityOption.VblankNMI_Delay=0;
  r->CompatibilityOption.MMC4_CountDelay=0;

  return r;


Error3:
  LL_Function->Thread.delete_MessageQueue(r->MessageQueue.Ack);
Error2:
  LL_Function->Thread.delete_MessageQueue(r->MessageQueue.Command);
Error1:
  Free(r);
Error0:
  return NULL;
}

/**
 * @brief 释放主机句柄
 *        !!!注意 需要主机完全停止后才能调用
 * @param h_nes 主机句柄
 * @return 无
 */
void delete_H_NES_Console(void* h_nes){

  CPU_DeInit(h_nes);
  PPU_DeInit(h_nes);
  APU_DeInit(h_nes);
  ((H_NES_Console*)h_nes)->Memory.Free(h_nes);
}

/**
 * @brief 设置电视标准 必须在主机未运行时使用
 * @param h_nes 主机句柄
 * @param TelevisionStandards 电视标准
 * @return 0:成功 其他:失败代号
 */
int H_NES_SetTelevisionStandards(void* h_nes,Hsize TelevisionStandards){
  if(((H_NES_Console*)h_nes)->IsRun!=0){
    return vH_NES_Result_Running;
  }
  ((H_NES_Console*)h_nes)->TelevisionStandards=TelevisionStandards;

  return vH_NES_Result_OK;
}

/**
 * @brief 根据映像头部16字节获取ROM信息
 * @param Header 头部16字节的头指针
 * @param PRG_ROM_Offset 用于返回PRG_ROM的偏移
 * @param PRG_ROM_Size 用于返回PRG_ROM的大小
 * @param CHR_ROM_Offset 用于返回CHR_ROM的偏移
 * @param CHR_ROM_Size 用于返回CHR_ROM的大小
 * @return 0:成功 其他:失败代号
 */
int H_NES_GetROM_Info(Hbyte* Header
  ,Huint32* PRG_ROM_Offset,Huint32* PRG_ROM_Size
  ,Huint32* CHR_ROM_Offset,Huint32* CHR_ROM_Size){
  return H_NES_Cartridge_GetROM_Info(Header,PRG_ROM_Offset,PRG_ROM_Size,CHR_ROM_Offset,CHR_ROM_Size);
}

/**
 * @brief 挂载ROM 须在主机未运行时使用
 * @param h_nes 主机句柄
 * @param NESROM ROM的指针, 指向.nes格式的内存区域
 * @return 0:成功 其他:失败代号
 */
int H_NES_LoadROM(void* h_nes,void* NESROM){
  if(((H_NES_Console*)h_nes)->IsRun!=0){
    return vH_NES_Result_Running;
  }
  return H_NES_Cartridge_LoadROM(h_nes,NESROM);
}

/**
 * @brief 分散挂载ROM 须在主机未运行时使用
 * @param h_nes 主机句柄
 * @param Header 头部16字节的头指针
 * @param PRG_ROM PRG_ROM头部指针
 * @param CHR_ROM CHR_ROM头部指针
 * @return 0:成功 其他:失败代号
 */
int H_NES_LoadROM_Scatter(void* h_nes,void* Header,void* PRG_ROM,void* CHR_ROM){
  if(((H_NES_Console*)h_nes)->IsRun!=0){
    return vH_NES_Result_Running;
  }
  return H_NES_Cartridge_LoadROM_Scatter(h_nes,Header,PRG_ROM,CHR_ROM);
}

/**
 * @brief 卸载ROM 必须在主机未运行时使用
 * @param h_nes 主机句柄
 * @return 无
 */
void H_NES_UnloadROM(void* h_nes){

  H_NES_Cartridge_UnloadROM(h_nes);
}

/**
 * @brief 主机运行 将完全占用调用其的线程 并且如果出现意外或者用户调用使其停止的API 线程会终止
 *        一般专门新建一个线程运行此方法
 * @param h_nes 主机句柄
 * @return 该方法不返回
 */
void H_NES_Start(void* h_nes){
  H_NES_Console* Console;
  H_NES_Message msg;

  Console=h_nes;

  while(0==Console->LL_Function.Thread.MessageTryReceive(Console->MessageQueue.Command,&msg)){
    //清空命令队列
  }

  msg.FunctionIndex=vH_NES_Message_FunctionIndex_Start;
  msg.v=NULL;
  Console->LL_Function.Thread.MessageSned(Console->MessageQueue.Ack,&msg);

  H_NES_Console_Start(h_nes);
}

/**
 * @brief 启动会调用H_NES_Start()的线程后必须调用 该方法用于等待H_NES运行
 * @param h_nes 主机句柄
 * @return 0:成功 其他:失败代号
 */
int H_NES_WaitUntilRun(void* h_nes){
  H_NES_Console* Console;
  H_NES_Message msg;

  Console=h_nes;

  if(Console->IsRun!=0){
    return vH_NES_Result_OK;
  }

  Console->LL_Function.Thread.MessageReceive(Console->MessageQueue.Ack,&msg);
  if(msg.FunctionIndex==vH_NES_Message_FunctionIndex_Start){
    Console->IsRun=-1;
    return vH_NES_Result_OK;
  }else if(msg.FunctionIndex==vH_NES_Message_FunctionIndex_Stop){
    return vH_NES_Result_Stopped;
  }else{
    for(;;){
      //不可能出现的情况
    }
  }
}

/**
 * @brief 获取主机是否在运行状态
 * @param h_nes 主机句柄
 * @return 0:未运行 其他:正在运行
 */
int H_NES_IsRun(void* h_nes){
  return ((H_NES_Console*)h_nes)->IsRun;
}

/**
 * @brief 停止主机
 * @param h_nes 主机句柄
 * @return 无
 */
void H_NES_Stop(void* h_nes){
  H_NES_Console* Console;
  H_NES_Message msg;

  Console=h_nes;

  if(Console->IsRun==0){
    return;
  }

  msg.FunctionIndex=vH_NES_Message_FunctionIndex_Stop;
  msg.v=NULL;
  if(0!=Console->LL_Function.Thread.MessageSned(Console->MessageQueue.Command,&msg)){
    for(;;){
      //不可能出现的情况
    }
  }

  do{
    Console->LL_Function.Thread.MessageReceive(Console->MessageQueue.Ack,&msg);
  }while(msg.FunctionIndex!=vH_NES_Message_FunctionIndex_Stop);
  Console->IsRun=0;
}

/**
 * @brief 获取进度数据大小 加载ROM之后才能调用 进度大小与ROM有关, 加载ROM之后才能通过本API获取进度数据大小
 * @param h_nes 主机句柄
 * @return 进度大小
 */
int H_NES_GetProgressSize(void* h_nes){
  return vH_NES_Console_ProgressSize+((H_NES_Console*)h_nes)->Cartridge.Progress.Size;
}

/**
 * @brief 获取进度 仅运行时可调用
 * @param h_nes 主机句柄
 * @param Progress 用于返回进度数据
 * @return 0:成功 其他:失败代号
 */
int H_NES_GetProgress(void* h_nes,void* Progress){
  H_NES_Message msg;

  msg.FunctionIndex=vH_NES_Message_FunctionIndex_GetProgress;
  msg.v=Progress;
  return Command(h_nes,&msg);
}

/**
 * @brief 加载进度 仅运行时可调用 H_NES并不会检查存档与ROM是否匹配, 如果尝试将存档加载到另一个游戏上, 可能会出现不可预料的后果(尤其是mapper不同的)
 * @param h_nes 主机句柄
 * @param Progress 进度数据
 * @return 0:成功 其他:失败代号
 */
int H_NES_LoadProgress(void* h_nes,void* Progress){
  H_NES_Message msg;

  msg.FunctionIndex=vH_NES_Message_FunctionIndex_LoadProgress;
  msg.v=Progress;
  return Command(h_nes,&msg);
}

/**
 * @brief 设置Vblank触发NMI的延迟
 * @param h_nes 主机句柄
 * @param VblankNMI_Delay 延迟PPU时钟数
 * @return 0:成功 其他:失败代号
 */
int H_NES_SetVblankNMI_Delay(void* h_nes,Hint32 VblankNMI_Delay){
  H_NES_Console* Console;
  H_NES_Message msg;
  int r;

  Console=h_nes;

  if(VblankNMI_Delay<0){
    return vH_NES_Result_ParamError;
  }

  msg.FunctionIndex=vH_NES_Message_FunctionIndex_SetVblankNMI_Delay;
  msg.v=&VblankNMI_Delay;
  r=Command(h_nes,&msg);

  if(r==vH_NES_Result_Stopped){
    Console->CompatibilityOption.MMC4_CountDelay=VblankNMI_Delay;
    return vH_NES_Result_OK;
  }
  
  return r;
}

/**
 * @brief 设置MMC4触发计数器的延迟
 * @param h_nes 主机句柄
 * @param MMC4_CountDelay 延迟PPU时钟数
 * @return 0:成功 其他:失败代号
 */
int H_NES_SetMMC4_CountDelay(void* h_nes,Hint32 MMC4_CountDelay){
  H_NES_Console* Console;
  H_NES_Message msg;
  int r;

  Console=h_nes;

  if(MMC4_CountDelay<0){
    return vH_NES_Result_ParamError;
  }

  msg.FunctionIndex=vH_NES_Message_FunctionIndex_SetMMC4_CountDelay;
  msg.v=&MMC4_CountDelay;
  r=Command(h_nes,&msg);

  if(r==vH_NES_Result_Stopped){
    Console->CompatibilityOption.MMC4_CountDelay=MMC4_CountDelay;
    return vH_NES_Result_OK;
  }
  
  return r;
}

/**
 * @brief 获取返回值 在NES终止后可调用
 * @param h_nes 主机句柄
 * @return 返回值
 */
int H_NES_GetRetVal(void* h_nes){
  return ((H_NES_Console*)h_nes)->RetVal;
}



